﻿using PmSoft.FormSchema.Attributes;
using PmSoft.FormSchema.Configs;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace PmSoft.FormSchema;

/// <summary>
/// 表单架构生成器 - 用于根据对象类型生成表单架构
/// </summary>
public static class FormSchemaGenerator
{
	private readonly static ComponentTypeInferer _componentInferer = new();

	/// <summary>
	/// 生成指定类型的表单架构（基础版本）
	/// </summary>
	/// <typeparam name="T">要生成表单架构的对象类型</typeparam>
	/// <param name="props">可选的表单属性配置，默认为 null</param>
	/// <returns>生成的表单架构响应对象</returns>
	public static FormSchemaResponse GenerateSchema<T>(FormProps? props = null) where T : class
	{
		return GenerateSchema(typeof(T), null, props);
	}

	/// <summary>
	/// 生成指定类型的表单架构，带有默认对象实例
	/// </summary>
	/// <typeparam name="T">要生成表单架构的对象类型</typeparam>
	/// <param name="obj">可选的对象实例，用于填充表单字段的默认值</param>
	/// <param name="props">可选的表单属性配置，默认为 null</param>
	/// <returns>生成的表单架构响应对象</returns>
	public static FormSchemaResponse GenerateSchema<T>(T? obj, FormProps? props = null) where T : class
	{
		return GenerateSchema(typeof(T), obj, props);
	}

	/// <summary>
	/// 根据表单名称从程序集中查找标记了 FormNameAttribute 的类型并生成表单架构
	/// </summary>
	/// <param name="formName">要查找的表单名称（不区分大小写）</param>
	/// <param name="assembly">可选的程序集，若为 null 则使用调用者的程序集</param>
	/// <param name="props">可选的表单属性配置，默认为 null</param>
	/// <returns>生成的表单架构响应对象</returns>
	/// <exception cref="ArgumentException">如果在程序集中未找到匹配 FormNameAttribute 的类型，则抛出异常</exception>
	public static FormSchemaResponse GenerateSchema(string formName, Assembly? assembly = null, FormProps? props = null)
	{
		assembly ??= Assembly.GetCallingAssembly();
		var type = ReflectionCacheManager.GetCachedTypeByFormName(formName, assembly);
		if (type == null)
			throw new ArgumentException($"在程序集中未找到标记为 '{formName}'（不区分大小写）的 FormNameAttribute 类型。");

		return GenerateSchema(type, null, props);
	}

	/// <summary>
	/// 根据表单名称和对象实例从程序集中查找类型并生成表单架构
	/// </summary>
	/// <param name="formName">要查找的表单名称（不区分大小写）</param>
	/// <param name="obj">可选的对象实例，用于填充默认值，必须与找到的类型兼容</param>
	/// <param name="assembly">可选的程序集，若为 null 则使用调用者的程序集</param>
	/// <param name="props">可选的表单属性配置，默认为 null</param>
	/// <returns>生成的表单架构响应对象</returns>
	/// <exception cref="ArgumentException">如果未找到匹配的类型或对象类型不兼容，则抛出异常</exception>
	public static FormSchemaResponse GenerateSchema(string formName, object? obj, Assembly? assembly = null, FormProps? props = null)
	{
		assembly ??= Assembly.GetCallingAssembly();
		var type = ReflectionCacheManager.GetCachedTypeByFormName(formName, assembly);
		if (type == null)
			throw new ArgumentException($"在程序集中未找到标记为 '{formName}'（不区分大小写）的 FormNameAttribute 类型。");

		if (obj != null && !type.IsInstanceOfType(obj))
			throw new ArgumentException($"提供的对象类型 '{obj.GetType().Name}' 与找到的类型 '{type.Name}' 不兼容。");

		return GenerateSchema(type, obj, props);
	}

	/// <summary>
	/// 核心方法：根据类型和对象生成表单架构
	/// </summary>
	/// <param name="type">要生成表单架构的类型</param>
	/// <param name="obj">可选的对象实例，用于填充默认值</param>
	/// <param name="props">可选的表单属性配置，默认为 null</param>
	/// <returns>生成的表单架构响应对象</returns>
	private static FormSchemaResponse GenerateSchema(Type type, object? obj, FormProps? props = null)
	{
		var properties = ReflectionCacheManager.GetCachedProperties(type);
		var schemaList = new List<FormSchema>();

		foreach (var prop in properties)
		{
			if (ShouldIgnoreProperty(prop))
				continue;

			var schema = CreateBaseSchema(prop);
			schema.Component = _componentInferer.InferComponentType(prop);
			schema.Type = _componentInferer.InferDataType(prop.PropertyType);

			ValidationRuleGenerator.AddValidationRules(schema, prop);
			ConfigureComponentProps(schema, prop);

			if (obj != null)
				schema.DefaultValue = prop.GetValue(obj);

			schemaList.Add(schema);
		}

		return CreateFormSchemaResponse(type, schemaList, props);
	}

	/// <summary>
	/// 检查属性是否需要忽略（是否有 IgnoreInFormAttribute 特性）
	/// </summary>
	/// <param name="prop">要检查的属性信息</param>
	/// <returns>如果属性标记为忽略则返回 true，否则返回 false</returns>
	private static bool ShouldIgnoreProperty(PropertyInfo prop) =>
		ReflectionCacheManager.GetCachedAttribute<IgnoreInFormAttribute>(prop) != null;

	/// <summary>
	/// 创建基础的表单架构对象
	/// </summary>
	/// <param name="prop">属性信息，用于生成表单字段</param>
	/// <returns>基础的表单架构对象</returns>
	private static FormSchema CreateBaseSchema(PropertyInfo prop)
	{
		var displayAttr = ReflectionCacheManager.GetCachedAttribute<DisplayAttribute>(prop);
		return new FormSchema
		{
			FieldName = prop.Name,
			Label = displayAttr?.Name ?? prop.Name,
			ComponentProps = new MaybeComponentProps(),
			Rules = new List<FormRule>()
		};
	}

	/// <summary>
	/// 创建表单架构响应对象
	/// </summary>
	/// <param name="type">生成表单架构的类型</param>
	/// <param name="schemaList">生成的表单字段架构列表</param>
	/// <param name="props">可选的表单属性配置，默认为 null</param>
	/// <returns>表单架构响应对象</returns>
	private static FormSchemaResponse CreateFormSchemaResponse(Type type, List<FormSchema> schemaList, FormProps? props = null)
	{
		var formNameAttr = type.GetCustomAttribute<FormNameAttribute>();
		return new FormSchemaResponse
		{
			FormName = formNameAttr?.Name ?? type.Name,
			Schema = schemaList,
			Props = props ?? new FormProps()
		};
	}

	/// <summary>
	/// 配置表单组件属性（例如选项、占位符等）
	/// </summary>
	/// <param name="schema">要配置的表单架构对象</param>
	/// <param name="prop">属性信息，用于推断组件属性</param>
	private static void ConfigureComponentProps(FormSchema schema, PropertyInfo prop)
	{
		var propType = prop.PropertyType;
		var underlyingType = Nullable.GetUnderlyingType(propType) ?? propType;

		// 检查是否有 FormComponentAttribute 特性，并注入其特有属性
		var componentAttribute = ReflectionCacheManager.GetCachedAttribute<FormComponentAttribute>(prop);
		if (componentAttribute != null)
		{
			componentAttribute.ConfigureComponentProps(schema);
		}

		// 处理枚举类型（无论是否有特性标记）
		if (underlyingType.IsEnum || (propType.IsArray && propType.GetElementType()?.IsEnum == true) || Nullable.GetUnderlyingType(propType)?.IsEnum == true)
		{
			var enumType = underlyingType.IsEnum ? underlyingType :
						   propType.IsArray ? propType.GetElementType()! :
						   Nullable.GetUnderlyingType(propType)!;

			var options = ReflectionCacheManager.GetCachedEnumOptions(enumType);
			if (Nullable.GetUnderlyingType(propType) != null &&
				ReflectionCacheManager.GetCachedAttribute<RequiredAttribute>(prop) == null)
			{
				options.Insert(0, new { Label = "请选择", Value = string.Empty });
			}
			schema.ComponentProps["options"] = options.ToArray();
			schema.ComponentProps["placeholder"] = "请选择";
		}
		// 输入框的占位符
		else if (schema.Component.Contains("Input"))
		{
			schema.ComponentProps["placeholder"] = $"请输入{schema.Label}";
		}
	}
}